package com.dyt.itool.generator;

import java.util.*;
import com.dyt.itool.model.*;
import com.dyt.itool.model.impl.*;

public class JavaHelper {

	public static final String NEWLINE = "\n";

	private Map<String, String> basicType = new HashMap<String, String>();
	private Map<String, String> basicWrapType = new HashMap<String, String>();

	public JavaHelper() {
		basicType.put("bool", "boolean");
		basicType.put("byte", "byte");
		basicType.put("short", "short");
		basicType.put("int", "int");
		basicType.put("long", "long");
		basicType.put("float", "float");
		basicType.put("double", "double");
		basicType.put("string", "String");

		basicWrapType.put("bool", "Boolean");
		basicWrapType.put("byte", "Byte");
		basicWrapType.put("short", "Short");
		basicWrapType.put("int", "Integer");
		basicWrapType.put("long", "Long");
		basicWrapType.put("float", "Float");
		basicWrapType.put("double", "Double");
		basicWrapType.put("string", "String");
	}

	public String getTypeName(IProperty property) {

		if (property.getType() == IProperty.PropertyType.BASIC) {
			return basicType.get(property.getTypeName());
		}

		if (property.getType() == IProperty.PropertyType.LIST) {
			ListProperty listProperty = (ListProperty) property;

			return "ArrayList<"
					+ getWrapTypeName(listProperty.getElementType()) + ">";
		}

		if (property.getType() == IProperty.PropertyType.MAP) {
			MapProperty mapProperty = (MapProperty) property;

			return "HashMap<" + getWrapTypeName(mapProperty.getKeyType()) + ","
					+ getWrapTypeName(mapProperty.getValueType()) + ">";
		}

		return property.getTypeName();
	}

	public String getWrapTypeName(IProperty property) {
		if (property.getType() == IProperty.PropertyType.BASIC) {
			return basicWrapType.get(property.getTypeName());
		}

		if (property.getType() == IProperty.PropertyType.LIST) {
			ListProperty listProperty = (ListProperty) property;

			return "ArrayList<"
					+ getWrapTypeName(listProperty.getElementType()) + ">";
		}

		if (property.getType() == IProperty.PropertyType.MAP) {
			MapProperty mapProperty = (MapProperty) property;

			return "HashMap<" + getWrapTypeName(mapProperty.getKeyType()) + ","
					+ getWrapTypeName(mapProperty.getValueType()) + ">";
		}

		return property.getTypeName();
	}



	private String getReadString(String basicType) {
		return getIoString("read", basicType);
	}

	private String getWriteString(String basicType) {
		return getIoString("write", basicType);
	}

	public String getIoString(String suffix, String basicType) {
		if ("string".equals(basicType)) {
			suffix += "UTF8";
		} else {
			char[] chars = basicType.toCharArray();
			chars[0] = Character.toUpperCase(chars[0]);
			suffix += new String(chars);
		}
		return suffix;
	}

	public String getSupperClass( Struct struct ){
		if( struct.hasSupperStruct() ){
			return " extends " + struct.getSupperStruct().getStructName();
		}else{
			return "";
		}
	}

	public String getVarDefine(IProperty property) {
		String define = "public " + getTypeName(property) + " "
				+ property.getVarName();

		if (property.getType() != IProperty.PropertyType.BASIC) {
            if( property.isOptinal() ) {
                define += " = null ";
            }else {
                define += " = new " + getTypeName(property) + " ( )";
            }
		}
		define += ";";

		return define;
	}

	public String getVarPackageDefine(IProperty property) {

		StringBuilder sb = new StringBuilder();
		sb.append(NEWLINE);
		sb.append("	public " + getTypeName(property));

		if (property.getTypeName().equals("boolean")
				|| property.getTypeName().equals("Boolean")) {
			sb.append(" is");
		} else {
			sb.append(" get");
		}
		sb.append(Character.toUpperCase(property.getVarName().charAt(0))
				+ property.getVarName().substring(1) + "() {");
		sb.append(NEWLINE);
		sb.append("		return " + property.getVarName() + ";");
		sb.append(NEWLINE);
		sb.append("	}");
		sb.append(NEWLINE);

		sb.append(NEWLINE);
		sb.append("	public void set");
		sb.append(Character.toUpperCase(property.getVarName().charAt(0))
				+ property.getVarName().substring(1) + "("
				+ getTypeName(property) + " " + property.getVarName() + ") {");
		sb.append(NEWLINE);
		sb.append("		this." + property.getVarName() + " = "
				+ property.getVarName() + ";");
		sb.append(NEWLINE);
		sb.append("	}");

		return sb.toString();
	}

	public String getReaderString(IProperty property) {

		if (property.getType() == IProperty.PropertyType.USERTYPE) {
			StringBuilder sb = new StringBuilder();
			sb.append("if( buffer.readBool( ) ){");
			sb.append(NEWLINE);

			sb.append( "			if( "  + property.getVarName( ) + " == null ){" );
			sb.append( NEWLINE );
			sb.append("				" + property.getVarName( ) +  " = new "  + getTypeName(property) + "(); " );
			sb.append( NEWLINE );
			sb.append( "			}" );

			sb.append( NEWLINE);
			sb.append("			" + property.getVarName()
					+ ".readFromBuffer( buffer );");
			sb.append(NEWLINE);
			sb.append("		}else{");
			sb.append(NEWLINE);
			sb.append("			" + property.getVarName() + " = null;");
			sb.append(NEWLINE);
			sb.append("		}");
			sb.append(NEWLINE);
			return sb.toString();

		} else if (property.getType() == IProperty.PropertyType.LIST) {

			ListProperty listProperty = (ListProperty) property;

			StringBuilder sb = new StringBuilder();
			sb.append("{");
			sb.append(NEWLINE);
			sb.append("    		int size = buffer.readInt();");
			sb.append(NEWLINE);
			sb.append("    		for( int i = 0; i < size; i++ ){");
			sb.append(NEWLINE);
			if (listProperty.getElementType().getType() == IProperty.PropertyType.BASIC) {
				sb.append("	       		"
						+ property.getVarName()
						+ ".add( buffer."
						+ getReadString(listProperty.getElementType()
								.getTypeName()) + "() );");
				sb.append(NEWLINE);
			} else {
				sb.append("        		"
						+ listProperty.getElementType().getTypeName()
						+ " model = new "
						+ listProperty.getElementType().getTypeName() + "( );");
				sb.append(NEWLINE);
				sb.append("        		model.readFromBuffer( buffer );");
				sb.append(NEWLINE);
				sb.append("				" + property.getVarName() + ".add( model ); ");
				sb.append(NEWLINE);
			}
			sb.append("    		}");
			sb.append(NEWLINE);
			sb.append("		}");
			return sb.toString();

		} else if (property.getType() == IProperty.PropertyType.MAP) {

			MapProperty mapProperty = (MapProperty) property;

			StringBuilder sb = new StringBuilder();
			sb.append("{");
			sb.append(NEWLINE);
			sb.append("    		int size = buffer.readInt();");
			sb.append(NEWLINE);
			sb.append("    		for( int i = 0; i < size; i++ ){");
			sb.append(NEWLINE);
			if (mapProperty.getKeyType().getType() == IProperty.PropertyType.BASIC) {
				sb.append("        		"
						+ getWrapTypeName(mapProperty.getKeyType())
						+ " key = buffer."
						+ getReadString(mapProperty.getKeyType().getTypeName())
						+ "( );");
				sb.append(NEWLINE);
			} else {
				sb.append("        		"
						+ getWrapTypeName(mapProperty.getKeyType())
						+ " key = new "
						+ mapProperty.getKeyType().getTypeName() + "( );");
				sb.append(NEWLINE);
				sb.append("        		key.readFromBuffer( buffer );");
				sb.append(NEWLINE);
			}

			if (mapProperty.getValueType().getType() == IProperty.PropertyType.BASIC) {
                sb.append("        		"
						+ getWrapTypeName(mapProperty.getValueType())
						+ " value = buffer."
						+ getReadString(mapProperty.getValueType()
								.getTypeName()) + "( );");
				sb.append(NEWLINE);
			} else {
				sb.append("        		"
						+ getWrapTypeName(mapProperty.getValueType())
						+ " value = new "
						+ mapProperty.getValueType().getTypeName() + "( );");
				sb.append(NEWLINE);
				sb.append("        		value.readFromBuffer( buffer );");
				sb.append(NEWLINE);
			}

			sb.append("        		" + property.getVarName()
					+ ".put( key, value ); ");
			sb.append(NEWLINE);
			sb.append("    			}");
			sb.append(NEWLINE);
			sb.append("		}");

			return sb.toString();
		} else {

			return property.getVarName() + " = buffer."
					+ getReadString(property.getTypeName()) + "( );";

		}
	}

	public String getWriterString(IProperty property) {

		if (property.getType() == IProperty.PropertyType.USERTYPE) {

			StringBuilder sb = new StringBuilder();
			sb.append("if( " + property.getVarName() + " != null ){");
			sb.append(NEWLINE);
			sb.append("			buffer.writeBool( true );");
			sb.append(NEWLINE);
			sb.append("			" + property.getVarName()
					+ ".writeToBuffer( buffer );");
			sb.append(NEWLINE);
			sb.append("		}else{");
			sb.append(NEWLINE);
			sb.append("			buffer.writeBool( false );");
			sb.append(NEWLINE);
			sb.append("		}");
			sb.append(NEWLINE);
			return sb.toString();

		} else if (property.getType() == IProperty.PropertyType.LIST) {

			ListProperty listProperty = (ListProperty) property;

			StringBuilder sb = new StringBuilder();
			sb.append("{");
			sb.append(NEWLINE);
			sb.append("    		int size;");
			sb.append(NEWLINE);
			sb.append("    		if( " + property.getVarName() + "==null ){");
			sb.append(NEWLINE);
			sb.append("    		    size = 0;");
			sb.append(NEWLINE);
			sb.append("    		}else{");
			sb.append(NEWLINE);
			sb.append("    		    size = " + property.getVarName() + ".size();");
			sb.append(NEWLINE);
			sb.append("    		}");
			sb.append(NEWLINE);
			sb.append("	    	buffer.writeInt( size ); ");
			sb.append(NEWLINE);
			sb.append("    		for( int i = 0; i < size; i++ ){");
			sb.append(NEWLINE);
			if (listProperty.getElementType().getType() == IProperty.PropertyType.BASIC) {
				sb.append("	       		buffer."
						+ getWriteString(listProperty.getElementType()
								.getTypeName()) + "( " + property.getVarName()
						+ ".get( i ) ); ");
				sb.append(NEWLINE);
			} else {
				sb.append("        		"
						+ listProperty.getElementType().getTypeName()
						+ " model = " + property.getVarName() + ".get( i ) ; ");
				sb.append(NEWLINE);

				//如果模型为空, 进行修补
				sb.append( "			if(model==null) {" );
				sb.append(NEWLINE);
				sb.append( "				model = new "+ listProperty.getElementType().getTypeName() + "( );");
				sb.append(NEWLINE);
				sb.append( "				System.err.println( this.getClass().getSimpleName() + \"."+ property.getVarName() + " 第\" + i + \"个元素为null\"   ); " );
				sb.append(NEWLINE);
				sb.append( "			}");
				sb.append(NEWLINE);

				sb.append("        		model.writeToBuffer( buffer );");
				sb.append(NEWLINE);
			}
			sb.append("    		}");
			sb.append(NEWLINE);
			sb.append("		}");
			return sb.toString();

		} else if (property.getType() == IProperty.PropertyType.MAP) {

			MapProperty mapProperty = (MapProperty) property;

			StringBuilder sb = new StringBuilder();
			sb.append("{");
			sb.append(NEWLINE);
			sb.append("    Set<" + getWrapTypeName(mapProperty.getKeyType())
					+ "> keySet = " + property.getVarName() + ".keySet();");
			sb.append(NEWLINE);
			sb.append("    buffer.writeInt( keySet.size( ) );");
			sb.append(NEWLINE);
			sb.append("    for( " + getTypeName(mapProperty.getKeyType())
					+ " key  : keySet ){");
			sb.append(NEWLINE);
			if (mapProperty.getKeyType().getType() == IProperty.PropertyType.BASIC) {
				sb.append("        buffer."
						+ getWriteString(mapProperty.getKeyType().getTypeName())
						+ "(  key );");
			} else {
				sb.append("        key.writeToBuffer( buffer );");
			}
			sb.append(NEWLINE);

			if (mapProperty.getValueType().getType() == IProperty.PropertyType.BASIC) {
				sb.append("        buffer."
						+ getWriteString(mapProperty.getValueType()
								.getTypeName()) + "( "
						+ mapProperty.getVarName() + ".get( key )  );");
			} else {
				sb.append("        " + mapProperty.getVarName()
						+ ".get( key ).writeToBuffer( buffer );");
			}
			sb.append(NEWLINE);

			sb.append("    }");
			sb.append(NEWLINE);

			sb.append("}");

			return sb.toString();

		} else {

			return "buffer." + getWriteString(property.getTypeName()) + "( "
					+ property.getVarName() + ");";
		}
	}


	public String firstUpper( String input ){
		if( input.length() > 0 ) {
			return Character.toUpperCase( input.charAt(0) ) + input.substring(1);
		}else {
			return input;
		}
	}

	public String firstLower( String input ){
		if( input.length() > 0 ) {
			return Character.toLowerCase( input.charAt(0) ) + input.substring(1);
		}else {
			return input;
		}
	}
}
